/**
 * @brief 一个简单的策略功能演示
 *
 * 说明：本演示代码需要接入外部行情，仅用于API功能演示用途，不能用于生产环境。
 *
 * 策略：根据行情数据选择买卖方向，每个方向的仓位最多持仓1手。
 *
 * 根据实时行情判断：
 * 1. 假如卖出量小于买入量，或卖出量大于买入量不足10手：
 *   1.1 如果持有空头仓位，那么以当前卖价平空头仓位；
 *   1.2 如果没有多头仓位，那么以当前买价建多头仓位；
 *
 * 2. 假如卖出量大于买入量超过10手：
 *   2.1 如果持有多头仓位，那么以当前买价平多头仓位；
 *   2.2 如果没有空头仓位，那么以当前卖价建空头仓位；
 *
 * 演示功能：
 * 1. 通过配置文件创建API实例；
 * 2. 启动API，启动成功后，调用login接口登录柜台；
 * 3. 等数据加载完毕后，实时接入外部行情；
 * 4. 根据行情和交易策略，进行报单；
 * 5. 经过1000次行情处理后，退出交易；
 */

#include <map>
#include "ExampleTrader.h"

class Example_02_Trader : public ExampleTrader {
public:
    Example_02_Trader() = default;

    ~Example_02_Trader() override {
        // release api.
        if (mApi) {
            mApi->stop();
            delete mApi;
            mApi = nullptr;
        }
    };

    void start() {
        if (mApi) {
            printf("error: trader has been started.\n");
            return;
        }

        mOrderLocalId = 0;
        mApi = makeXTFApi(mConfigPath.c_str());
        if (mApi == nullptr) {
            printf("error: create xtf api failed, please check config: %s.\n", mConfigPath.c_str());
            exit(0);
        }

        printf("api version: %s.\n", getXTFVersion());
        int ret = mApi->start(this);
        if (ret != 0) {
            printf("start failed, error code: %d\n", ret);
            exit(0);
        }
    }

    void stop() {
        if (!mApi) {
            printf("error: trader is not started.\n");
            return;
        }

        int ret = mApi->stop();
        if (ret == 0) {
            // API停止操作是异步操作，需要等待一定时间，以防API对象回调时失效。
            // 4.1.664及更高版本不存在此问题，不需要增加延时。
            usleep(100000);

            delete mApi;
            mApi = nullptr;
        } else {
            printf("api stop failed, error code: %d\n", ret);
        }
    }

    void onStart(int errorCode, bool isFirstTime) override {
        ExampleTrader::onStart(errorCode, isFirstTime);

        if (errorCode == 0) {
            if (isFirstTime) {
                // TODO: init something if needed.
            }

            //mApi->login(mUsername.c_str(), mPassword.c_str(), mAppID.c_str(), mAuthCode.c_str());
            int ret = mApi->login();
            if (ret != 0) {
                printf("api logging in failed, error code: %d\n", ret);
            }
        } else {
            printf("error: api start failed, error code: %d.\n", errorCode);
        }
    }

    void onStop(int reason) override {
        ExampleTrader::onStop(reason);
        printf("api stopped, reason: %d.\n", reason);
    }

    void onLogin(int errorCode, int exchangeCount) override {
        ExampleTrader::onLogin(errorCode, exchangeCount);

        if (errorCode != 0) {
            printf("error: login failed, error code: %d.\n", errorCode);
            return;
        }

        printf("login success.\n");
    }

    void onLogout(int errorCode) override {
        ExampleTrader::onLogout(errorCode);

        if (errorCode != 0) {
            printf("error: logout failed, error code: %d.\n", errorCode);
            return;
        }

        printf("logout success.\n");
    }

    void onChangePassword(int errorCode) override {
        if (errorCode != 0) {
            printf("error: change password failed, error code: %d.\n", errorCode);
            return;
        }

        printf("change password success.\n");
    }

    void onReadyForTrading(const XTFAccount *account) override {
        ExampleTrader::onReadyForTrading(account);

        if (!mApi) return;
        mOrderLocalId = account->lastLocalOrderID;

        // 静态数据初始化完毕后回调，传递对象指针，是为了用户保存指针对象，方便使用。
        // 这里可以开始做交易。
        mInstrument = mApi->getInstrumentByID(mInstrumentID.c_str());
        if (!mInstrument) {
            printf("error: instrument not found: %s.\n", mInstrumentID.c_str());
            return;
        }

        int ret = mApi->subscribe(mInstrument);
        if (ret != 0) {
            printf("subscribe instrument %s failed, error code: %d\n", mInstrumentID.c_str(), ret);
        }
    }

    void onLoadFinished(const XTFAccount *account) override {
        ExampleTrader::onLoadFinished(account);

        // 流水数据追平后回调
        printf("info: data load finished.\n");
    }

    void onOrder(int errorCode, const XTFOrder *order) override {
        // 报单失败。根据报单错误码判断是柜台拒单，还是交易所拒单。
        if (errorCode != 0) {
            switch (order->actionType) {
                case XTF_OA_Insert:
                    printf("insert order failed, error: %d\n", errorCode);
                    break;
                case XTF_OA_Return:
                    printf("return order failed, error: %d\n", errorCode);
                    break;
                default:
                    printf("order action(%d) failed, error: %d.\n",
                           order->actionType, errorCode);
                    break;
            }
            return;
        }

        // 收到报单回报。根据报单状态判断处理逻辑。
        switch (order->orderStatus) {
            case XTF_OS_Accepted:
                printf("order accepted.\n");
                mOrders[order->localOrderID] = order; // 保存报单对象
                break;
            case XTF_OS_AllTraded:
                printf("order all traded.\n");
                break;
            case XTF_OS_Queuing:
                printf("order queuing.\n");
                break;
            case XTF_OS_Rejected:
                printf("order rejected.\n");
                break;
            default:
                break;
        }
    }

    void onCancelOrder(int errorCode, const XTFOrder *cancelOrder) override {
        // 撤单失败。根据报单错误码判断是柜台拒单，还是交易所拒单。
        if (errorCode != 0) {
            printf("error: cancel order failed, sys-id: %d.\n", cancelOrder->sysOrderID);
            return;
        }
        printf("order canceled, sys-id: %d.\n", cancelOrder->sysOrderID);
    }

    void onTrade(const XTFTrade *trade) override {
        // 收到交易回报
        printf("recv trade, sys-order-id: %d, trade-id: %ld.\n", trade->order->sysOrderID, trade->tradeID);
    }

    void onAccount(int event, int action, const XTFAccount *account) override {
        // 账户信息发生变化时回调该接口，如：出入金变化
        if (event == XTF_EVT_AccountCashInOut) {
            if (action == XTF_CASH_In) printf("cash in.\n");
            if (action == XTF_CASH_Out) printf("cash out.\n");
        } else {
            printf("account is changed: event=%d, action=%d, accountID=%s\n",
                   event, action, account->accountID);
        }
    }

    void onExchange(int event, int channelID, const XTFExchange *exchange) override {
        // 交易所信息发生变化时回调该接口，如：交易所前置变化
        printf("exchange is changed: event=%d, channelID=%d, exchange=%s\n",
               event, channelID, exchange->exchangeID);
    }

    void onInstrument(int event, const XTFInstrument *instrument) override {
        // 合约属性发生变化时回调该接口，如：状态变化
        if (event == XTF_EVT_InstrumentStatusChanged) {
            printf("instrument status changed: %s %d.\n",
                   instrument->instrumentID, instrument->status);
        }
    }

    void onBookUpdate(const XTFMarketData *marketData) override {
        // 行情回调接口，根据行情触发交易策略
        doTrade(*marketData);
    }

    void onEvent(const XTFEvent &event) override {
        printf("recv event: %d.\n", event.eventID);
    }

    void onError(int errorCode, void *data, size_t size) override {
        printf("something is wrong, error code: %d.\n", errorCode);
    }

    void updateBook(const char *instrumentID,
                    double lastPrice,
                    double bidPrice,
                    int bidVolume,
                    double askPrice,
                    int askVolume) {
        if (!mApi) {
            printf("error: api is not started.\n");
            return;
        }

        const XTFInstrument *instrument = mApi->getInstrumentByID(instrumentID);
        if (!instrument) {
            printf("error: instrument is not found: %s.\n", instrumentID);
            return;
        }

        int ret = mApi->updateBook(instrument, lastPrice, bidPrice, bidVolume, askPrice, askVolume);
        if (ret != 0) {
            printf("error: update market data failed, error code: %d\n", ret);
        }
    }

private:
    void doTrade(const XTFMarketData &marketData) {
        if (marketData.getInstrument() != mInstrument) {
            return;
        }

        double askPrice = marketData.askPrice;
        int askVolume = marketData.askVolume;
        double bidPrice = marketData.bidPrice;
        int bidVolume = marketData.bidVolume;
        if (askVolume - bidVolume <= 10) { // buy
            if (mInstrument->getShortPosition()->position >= 1) {
                closeShort(askPrice, 1);
                printf("close short position: 1.\n");
                return;
            }

            if (mInstrument->getLongPosition()->position <= 0) {
                openLong(askPrice, 1);
                printf("open long position: 1.\n");
                return;
            }
        }

        if (askVolume - bidVolume >= 10) { // sell
            if (mInstrument->getLongPosition()->position >= 1) {
                closeLong(bidPrice, 1);
                printf("close long position: 1.\n");
                return;
            }

            if (mInstrument->getShortPosition()->position <= 0) {
                openShort(bidPrice, 1);
                printf("open short position: 1.\n");
                return;
            }
        }
    }

    int openLong(double price, uint32_t volume) {
        if (!mApi) return -1;
        XTFInputOrder order{};
        memset(&order, 0, sizeof(order));
        order.instrument = mInstrument;
        order.direction = XTF_D_Buy;
        order.offsetFlag = XTF_OF_Open;
        order.orderType = XTF_ODT_FOK;
        order.price = price;
        order.volume = volume;
        order.channelSelectionType = XTF_CS_Auto;
        order.localOrderID = ++mOrderLocalId;
        return mApi->insertOrder(order);
    }

    int closeLong(double price, uint32_t volume) {
        if (!mApi) return -1;
        XTFInputOrder order{};
        memset(&order, 0, sizeof(order));
        order.instrument = mInstrument;
        order.direction = XTF_D_Buy;
        order.offsetFlag = XTF_OF_Close;
        order.orderType = XTF_ODT_FOK;
        order.price = price;
        order.volume = volume;
        order.channelSelectionType = XTF_CS_Auto;
        order.localOrderID = ++mOrderLocalId;
        return mApi->insertOrder(order);
    }

    int openShort(double price, uint32_t volume) {
        if (!mApi) return -1;
        XTFInputOrder order{};
        memset(&order, 0, sizeof(order));
        order.instrument = mInstrument;
        order.direction = XTF_D_Sell;
        order.offsetFlag = XTF_OF_Open;
        order.orderType = XTF_ODT_FOK;
        order.price = price;
        order.volume = volume;
        order.channelSelectionType = XTF_CS_Auto;
        order.localOrderID = ++mOrderLocalId;
        return mApi->insertOrder(order);
    }

    int closeShort(double price, uint32_t volume) {
        if (!mApi) return -1;
        XTFInputOrder order{};
        memset(&order, 0, sizeof(order));
        order.instrument = mInstrument;
        order.direction = XTF_D_Sell;
        order.offsetFlag = XTF_OF_Close;
        order.orderType = XTF_ODT_FOK;
        order.price = price;
        order.volume = volume;
        order.channelSelectionType = XTF_CS_Auto;
        order.localOrderID = ++mOrderLocalId;
        return mApi->insertOrder(order);
    }

    int cancelOrder(int orderLocalId) {
        auto iter = mOrders.find(orderLocalId);
        if (iter == mOrders.end()) {
            printf("order not found: %d\n", orderLocalId);
            return -1;
        }
        const XTFOrder *order = iter->second;
        return mApi->cancelOrder(order);
    }

private:
    const XTFInstrument *mInstrument;
    std::map<int, const XTFOrder *> mOrders;
    int mOrderLocalId;
};


/**
 * @brief 一个简单的策略，根据行情数据选择买卖方向，每个方向的仓位最多持仓1手。
 *
 * @param configPath
 * @param instrumentId
 */
void runExample(const std::string &configPath, const std::string &instrumentId) {
    printf("start example 02.\n");

    Example_02_Trader trader;
    trader.setConfigPath(configPath);
    trader.setInstrumentID(instrumentId);

    trader.start();

    bool isStop = false;
    int tickCount = 0;
    while (!isStop) {
        if (trader.isLoadFinished()) {
            // TODO: 修改下面的代码，接入外部行情，通过外部行情，驱动策略进行报单。
            double bidPrice = 300.50;
            double askPrice = 301.50;
            double lasPrice = 301.00;
            int bidVolume = 24;
            int askVolume = 38;
            trader.updateBook(instrumentId.c_str(),
                              lasPrice,
                              bidPrice,
                              bidVolume,
                              askPrice,
                              askVolume);
        }

        printf("sleep 500ms.\n");
        usleep(500000); // sleep 500ms
        if (tickCount++ > 1000) {
            isStop = true;
        }
    }

    trader.stop();
}

int main(int argc, const char *argv[]) {
    // TODO: 解析传入参数，提取相关的配置
    std::string configPath = "../config/xtf_trader_api.config";
    std::string instrumentId = "au2212";
    runExample(configPath, instrumentId);
    return 0;
}
